(*  :Title:	Continuous Time Fourier Transform  *)

(*  :Authors:	Brian Evans, Wallace McClure, James McClellan  *)

(*  :Summary:	*)

(*  :Context:	SignalProcessing`Analog`Fourier`  *)

(*  :PackageVersion:  2.7	*)

(*
    :Copyright:	Copyright 1990-1991 by Brian L. Evans
		Georgia Tech Research Corporation

	Permission to use, copy, modify, and distribute this software
	and its documentation for any purpose and without fee is
	hereby granted, provided that the above copyright notice
	appear in all copies and that both that copyright notice and
	this permission notice appear in supporting documentation,
	and that the name of the Georgia Tech Research Corporation,
	Georgia Tech, or Georgia Institute of Technology not be used
	in advertising or publicity pertaining to distribution of the
	software without specific, written prior permission.  Georgia
	Tech makes no representations about the suitability of this
	software for any purpose.  It is provided "as is" without
	express or implied warranty.
 *)

(*
    :History:	start -- February 22, 1990
		made into package -- April 18, 1990
		added Dialogue ability -- October, 1990
		added many more transform pairs -- February, 1991
		incorporated duality so only one set of transform
		  pairs, properties, etc., is encoded --  December, 1991
 *)

(*  :Keywords:	Fourier transform, frequency response  *)

(*
    :Source:	{Signals and Systems} by A. V. Oppenheim and A. S. Willsky.
		  Prentice Hall.
		{The Fourier Transform and Its Applications} by R. Bracewell
 *)

(*  :Warning:	*)

(*  :Mathematica Version:  1.2 or 2.0  *)

(*
    :Limitation:   If the function to  be transformed does not match either
		   a specific Fourier transform or the LaPlace transform, a 
		   nasty expression is returned.
 *)

(*
    :Discussion: When loaded into Mathematica,  this package will allow the 
		 computation of  continuous  Fourier  and  inverse  Fourier
		 transforms.   It  turns  out  that in order to show a user
		 each step  of  transformation process, the transform rules
		 needed to be placed in a list of deferred (delayed) rules.

	MyFourier[f, t, w, s] computes  the  one-dimensional Fourier trans-
	form  of  f  using  t  as  the time variable and w as the frequency
	variable.

	MyInverseCTFT[f, w, t, s] computes  the   inverse   one-dimensional
	continuous-time Fourier transform using w as the frequency variable
	and t as the time variable.

	The Fourier  transform  formulas  used  follow Oppenheim & Willsky.
	To convert Bracewell's formulas,  which use 2 Pi s x instead of w t
	in the exponential function,

	  (1) use  x = t  and  s = w / (2 Pi)  OR
	  (2) use  s = w  and  x = t / (2 Pi)  and  divide the time
	      function by 2 Pi.
	      
	Note that in (2), dividing the time function by 2 Pi is equivalent
	to multiplying the frequency response by 2 Pi.   Either way places
	the 1/(2 Pi) normalization term in front of the inverse formula.
 *)

(*  :Functions:	CTFTransform InvCTFTransform  *)



(*  B E G I N     P A C K A G E  *)

BeginPackage[ "SignalProcessing`Analog`Fourier`",
	      "SignalProcessing`Analog`LaPlace`",
	      "SignalProcessing`Analog`InvLaPlace`",
	      "SignalProcessing`Analog`LSupport`",
	      "SignalProcessing`Digital`DSupport`",
	      "SignalProcessing`Support`TransSupport`",
	      "SignalProcessing`Support`ROC`",
	      "SignalProcessing`Support`SigProc`",
	      "SignalProcessing`Support`SupCode`" ]


If [ TrueQ[ $VersionNumber >= 2.0 ],
     Off[ General::spell ];
     Off[ General::spell1 ] ];


(*  U S A G E     I N F O R M A T I O N  *)

CTFTData::usage =
	"CTFTData is the data-tag for the CTFT."

CTFTransform::usage =
	"CTFTransform[f, t] or CTFTransform[f, t, w] computes the \
	continuous-time Fourier transform of f. \
	It has time argument(s) t and frequency argument(s) w."

InvCTFTransform::usage =
	"InvCTFTransform[f, w, t] computes the inverse continuous time \
	Fourier transform of f with frequency arguments w and time \
	arguments t."

(*  E N D     U S A G E     I N F O R M A T I O N  *)


Begin[ "`Private`" ]


(*  Messages  *)

Transform::laplace = 
	"The Region of Convergence in the Laplace transform `` does not contain the imaginary axis so it cannot be converted into a Fourier transform."


(*  Options for the forward and inverse Fourier transforms  *)

displayFixUp[ op_ ] :=
	Append[ fixUp[op], TransformLookup -> Replace[TransformLookup, op] ]

CTFTransform/: Options[ CTFTransform ] :=
	displayFixUp[ Join[ { Definition -> False, Dialogue -> False },
			    Options[ InvLaPlace ] ] ]

InvCTFTransform/: Options[ InvCTFTransform ] :=
	displayFixUp[ Join[ { Definition -> False,
			      Dialogue -> False,
			      Terms -> False},
			    Options[ InvLaPlace ] ] ]


(*  Printed forms of local objects so that Dialogue -> All works neatly  *)

Format[ dualFT[ x_, w_, rest__, InvCTFTransform ] ] :=
	SequenceForm[ ColumnForm[{"F" Superscript[-1],
				  "  " ~StringJoin~ ToString[w]}],
		      { x } ]

Format[ dualFT[ x_, t_, rest__, CTFTransform ] ] :=
	SequenceForm[ ColumnForm[{"F",
				  "  " ~StringJoin~ ToString[t]}],
		      { x } ]

Format[ derivative[x_, w_Symbol, k_] ] := 
	SequenceForm[ ColumnForm[{"D" Superscript[k],
				  "  " ~StringJoin~ ToString[w]}],
		      { x } ]

Format[ integrate[x_, limits_] ] := 
	SequenceForm[ "Integrate[", x, ", ", limits, "]" ]

Format[	substitute[x_, w_, wnew_] ] :=
	SequenceForm[ {x}, Subscript[w -> wnew] ]

Format[	CTFTtoL[x_, w_, s_] ] :=
	SequenceForm[ {x}, Subscript[w -> s / I] ]

Format[ SignalProcessing`Analog`Fourier`Private`s ] := "s"


(*  State definition for the unified rule base *)

expandfield = 1		(* apply expand to the expression		*)
forlaplacefield = 2	(* all else has failed so try forward Laplace	*)
invlaplacefield = 3	(* all else has failed so try inverse Laplace	*)
definitionfield = 4	(* apply the Fourier transform definition	*)

statevariables = 4

(*  Global variables  *)
origVars = {}
tranVars = {}

(*  Supporting routines  *)

explain[ dualFT[x_, t_, rest__, CTFTransform]] :=
	Message[ Transform::incomplete, "forward Fourier transform", x, t ]
explain[ dualFT[x_, w_, rest__, InvCTFTransform]] :=
	Message[ Transform::incomplete, "inverse Fourier transform", x, w ]

fixUp[ op_ ] := { Apart -> Replace[Apart, op],
		  Definition -> Replace[Definition, op],
		  Dialogue -> Replace[Dialogue, op],
		  Simplify -> Replace[Simplify, op],
		  Terms -> Replace[Terms, op] }

initstate[] := Table[True, {statevariables}]
initstate[ CTFTransform ] :=
	SetStateField[ initstate[], invlaplacefield, False ]
initstate[ InvCTFTransform ] :=
	SetStateField[ initstate[], forlaplacefield, False ]

LtoCTFT[ SignalProcessing`Analog`LaPlace`Private`mylaplace[x_, lrest__], t_, w_, s_, rest__ ] :=
	CTFTtoL[ dualFT[x, t, w, s, rest], w, s ]
LtoCTFT[ x_, t_, w_, s_, trest__ ] := x

mustbenegative[x_] := ! TrueQ[ Positive[x] ]
mustbepositive[x_] := ! TrueQ[ Negative[x] ]

posttransform[x_] := ( x /. postrules )
postrules = {
	derivative[x_, t_, 1] :>
		D[ posttransform[x], t ],
	derivative[x_, t_, k_Integer] :>
		D[ posttransform[x], {t, k} ],
	integrate[x_, limits_] :>
		Integrate[ Expand[posttransform[x]], limits ],
	substitute[x_, -v_, v_] :>
		( posttransform[x] /. v -> -v ),
	substitute[x_, t_, tnew_] :>
		( posttransform[x] /. t -> tnew ),
	CTFTtoL[x_] :>
		Transform[ x, -Infinity, Infinity ],
	CTFTtoL[x_, w_, s_] :>
		Transform[ substitute[x, w, s / I], -Infinity, Infinity ],
	(a_ + b_. Sign[x_]) :> 2 a CStep[-x] /; ( a == -b ),
	(a_ + a_. Sign[x_]) :> 2 a CStep[x]
}

ROCincludesImagAxis[ trans_, op_ ] :=
	Block [	{cond, rm, rp, zerocond},
		rm = GetRMinus[trans];
		rp = GetRPlus[trans];
		cond = ( Negative[rm] && Positive[rp] );
		zerocond = ( ZeroQ[rm] || ZeroQ[rp] );
		If [ zerocond && SameQ[ Replace[Dialogue, op], All ],
		     Print[ "assuming that the origin which is an endpoint" ];
		     Print[ "of the region of convergence (ROC) of the" ];
		     Print[ "Laplace transform is included in the ROC" ] ];
		If [ cond || zerocond,
		     True,
		     False,
		     Assuming[cond, op]; True ] ]

transform[dualFT[x_, rest__]] := Replace[dualFT[x, rest], DualFTRules]
transform[x_] := x

validvarQ[ x_Symbol ] := True
validvarQ[ x_List ] := Apply[And, Map[VariableQ, x]]
validvarQ[ x_ ] := False


(*  Extension of Fourier transform operators  *)

Unprotect[FT, InvFT]
FT/: TheFunction[ FT[t_, w_] [f_] ] := CTFTransform[f, t, w]
InvFT/: TheFunction[ InvFT[w_, t_] [f_] ] := InvCTFTransform[f, w, t]
Protect[FT, InvFT]


(*  Extension of TheFunction operator  *)

CTFTData/: TheFunction[ CTFTData[x_, var_] ] := x


(*  Magnitude/Phase plot of a CTFT object *)

mppoptions = {	Domain -> Continuous, DomainScale -> Linear,
		MagRangeScale -> Log, PhaseRangeScale -> Degree,
		PlotRange -> All }

CTFTData/: MagPhasePlot[ CTFTData[trans_, vars_], rest___ ] :=
	Apply [	MagPhasePlot, { trans, rest } ~Join~ mppoptions ]


(*  One-dimensional Fourier transform driver  *)

DualFT1d[ f_, t_, w_, s_, tlist_, wlist_, oplist_, dir_ ] :=
	Block [	{different, newexpr, newrules, oldexpr, simops, state, trace},

		Off[ Power::infy ];
		trace = SameQ[ Replace[Dialogue, oplist], All ];
		state = initstate[dir];

		(*  Generate the 1-D FT rules  *)
		newrules = TransformFixUp[ t, w, oplist, dualFT, False,
					   dir, Null, Null ];
                If [ SameQ[dir, InvCTFTransform],
		     newrules = newrules /. w -> -w ];
		DualFTRules = Join[ newrules, OriginalDualFTRules ];

		(*  Embed the function in the dual ctft object  *)
		If [ trace && SameQ[dir, InvCTFTransform],
		     Print[ "The Duality Theorem will be used to" ];
		     Print[ "find the inverse Fourier transform:" ] ];
		oldexpr = dualFT[f, t, w, s, fixUp[oplist], state, dir];
		If [ SameQ[dir, InvCTFTransform],
		     oldexpr = dualFT[ f, t, w, s, fixUp[oplist],
		     		       state, CTFTransform ];
		     oldexpr = substitute[ oldexpr, w, -w ] / (2 Pi);
		     If [ trace, Print[oldexpr]; Print["which becomes"] ] ];

		(*  Simplify the function before transformation  *)

		simops = { Dialogue -> trace, Variables -> tlist };
		If [ TrueQ[ $VersionNumber >= 2.0 ],
		     PrependTo[simops, Trig -> False] ];
		newexpr = SPSimplify[ oldexpr, simops ];

		(*  Transform the function  *)
		different = True;
		If [ trace, Print[newexpr]; Print["which becomes"] ];
		While [ different,
			oldexpr = newexpr;
			newexpr = oldexpr /. ( dualFT[a__] :>
				  Replace[dualFT[a], DualFTRules] );
			different = ! SameQ[newexpr, oldexpr];
			If [ different && trace,
			     Print[newexpr]; Print["which becomes"] ] ];

		(*  Fix up the transform  *)
		different = True;
		While [ different,
			oldexpr = newexpr;
			newexpr = posttransform[oldexpr];
			different = ! SameQ[newexpr, oldexpr];
			If [ different && trace,
			     Print[newexpr]; Print["which becomes"] ] ];

		(*  Check for invalid Laplace transform substitutions  *)
		newexpr /. LTransData[x_, rest__] :>
		     		Message[ Transform::laplace, x ];

		(*  Simplify the resulting transform  *)
		If [ trace, Print[newexpr] ];
		newexpr = SPSimplify[ newexpr,
				      Variables -> wlist,
				      Dialogue -> trace ];

		On[ Power::infy ];

		newexpr ]




(*  B E G I N     F O R W A R D     F O U R I E R     T R A N S F O R M  *)


(*  CTFTransform  *)
CTFTransform[ f_ ] :=
	Message[ Transform::novariables, "t (time)", GetVariables[f] ]
CTFTransform[ f_, t_ ] :=
	CTFTransform[ f, t, DummyVariables[Length[t], Global`w] ]
CTFTransform[ f_, t_, w_, options___ ] :=
	Message[ Transform::badvar, "time", CTFTransform, t ] /;
	! validvarQ[t]
CTFTransform[ f_, t_, w_, options___ ] :=
	Message[ Transform::badvar, "frequency", CTFTransform, w ] /;
	! validvarQ[w]
CTFTransform[ f_, t_, w_, options___ ] :=
	Block[ {op, svars, trans, vars},
		vars = Which [ Length[t] > Length[w],
				 DummyVariables[Length[t], Global`w],
			       Length[t] < Length[w],
				 Take[w, Length[t]],
			       True,
				 w ];
		svars = DummyVariables[Length[t], s];

		origVars = t;
		tranVars = vars;
		op = ToList[options] ~Join~ Options[CTFTransform];
		trans = If [ Length[t] > 1, 
			     MultiDCTFT[f, t, vars, svars, op],
			     MyFourier[f, t, vars, svars, op] ]; 	

		If [ InformUserQ[op],
		     Scan[explain, trans, Infinity] ];

		CTFTData[ FourierSimplify[trans], FVariables[vars] ] ]

(*  Multidimensional continuous-time Fourier transform  *)
MultiDCTFT[f1_, t_, w_, s_, op_] :=
	Block [	{i, func = f1, length},
		length = Length[t];
		For [ i = 1, i <= length, i++,
		      func = MyFourier[ func, t[[i]], w[[i]],
					s[[i]], t, w, op ] ];
		func ]

(*  One-dimensional continuous-time Fourier transform  *)
MyFourier[ f_, t_, w_, s_, oplist_ ] :=
	DualFT1d[ f, t, w, s, {t}, {w}, oplist, CTFTransform ]

MyFourier[ f_, t_, w_, s_, tlist_, wlist_, oplist_ ] :=
	DualFT1d[ f, t, w, s, tlist, wlist, oplist, CTFTransform ]


(*  E N D     F O R W A R D     F O U R I E R     T R A N S F O R M  *)




(*  B E G I N     I N V E R S E     F O U R I E R     T R A N S F O R M  *)


(*  InvCTFTransform  *)
InvCTFTransform[ CTFTData[x_, FVariables[w_]] ] := InvCTFTransform[x, w]

InvCTFTransform[ CTFTData[x_, FVariables[w_]], rest__ ] :=
	InvCTFTransform[x, rest]

InvCTFTransform[ x_, w_ ] :=
	InvCTFTransform[ x, w, DummyVariables[Length[w], Global`t] ]

InvCTFTransform[ f_, w_, t_, options___ ] :=
	Message[ Transform::badvar, "frequency", InvCTFTransform, w ] /;
	! validvarQ[w]

InvCTFTransform[ f_, w_, t_, options___ ] :=
	Message[ Transform::badvar, "time", InvCTFTransform, t ] /;
	! validvarQ[t]

InvCTFTransform[ f_, w_, t_, options___ ] :=
	Block[ {op, s, svars, trans, vars},
		op = ToList[options] ~Join~ Options[InvCTFTransform];

		vars = Which [ Length[w] > Length[t],
				 DummyVariables[Length[w], Global`t],
			       Length[w] < Length[t],
				 Take[t, Length[w]],
			       True,
				 t ];

		svars = DummyVariables[Length[w], s];
		origVars = w;
		tranVars = vars;
		trans = If [ Length[w] > 1, 
			     MultiDInvCTFT[f, w, vars, svars, op],
			     MyInverseCTFT[f, w, vars, svars, op] ];

		If [ InformUserQ[op],
		     Scan[explain, trans, Infinity] ];

		FourierSimplify[trans] ]

(*  MultiDInvCTFT  *)
MultiDInvCTFT[ f1_, w_, t_, s_, op_ ]:= 
	Block [	{i, func = f1, length},
		length = Length[t];
		For [ i = 1, i <= length, i++,
		      func = MyInverseCTFT[ func, w[[i]], t[[i]],
					    s[[i]], w, t, op ] ];
		func ]

(*  driver for 1-D inverse CTFT rules  *)
MyInverseCTFT[ f_, w_, t_, s_, op_ ] :=
	DualFT1d[ f, w, t, s, {w}, {t}, op, InvCTFTransform ]

MyInverseCTFT[ f_, w_, t_, s_, wlist_, tlist_, op_ ] :=
	DualFT1d[ f, w, t, s, wlist, tlist, op, InvCTFTransform ]





(*  E N D     I N V E R S E     F O U R I E R     T R A N S F O R M  *)




(*  B E G I N     D U A L     R U L E     B A S E  *)

DualFTRules = {}

OriginalDualFTRules = {

(*      Clean up any calls to the Laplace transform rule bases		  *)
(*      Must go before any other rules to avoid spurious Delta functions  *)

dualFT[ LTransData[x_, rest__], t_, w_, s_, op_, __ ] :>
	If [ ROCincludesImagAxis[ LTransData[x, rest], op ],
	     x /. s -> I w,
	     LTransData[x, rest] ] ,

dualFT[ SignalProcessing`Analog`InvLaPlace`Private`myinvlaplace[x_,
		s_, restL__], t_, w_, s_, rest__ ] :>
	dualFT[ x /. s -> I t, t, w, s, rest ],


(*  A.  Transform pairs			*)
(*	 1.  constant functions		*)
dualFT[ 0, t_, w_, __ ] :> 0,
dualFT[ c_, t_, w_, __ ] :> 2 Pi c Delta[w] /; FreeQ[c, t],

(*	 2.  two delta functions	*)
(*	     c delta(t + a) + d delta(t + b) where c = -d and a = -b	*)
(*	     c delta(t - b) - c delta(t + b) <---> -2 I c sin(b w) 	*)
dualFT[ c_. Delta[t_ + a_] + d_. Delta[t_ + b_], t_, w_, __ ] :>
	-2 I c Sin[b w] /;
	( a == -b ) && ( c == 1 || c == -1 ) && ( c == -d ) && FreeQ[{a, c}, t],

(*	     c delta(t + a) + c delta(t + b) where c = -d and a = -b	*)
(*	     c delta(t - b) + c delta(t + b) <---> 2 c cos(b w) 	*)
dualFT[ c_. Delta[t_ + a_] + c_. Delta[t_ + b_], t_, w_, __ ] :>
	2 c Cos[b w] /;
	( a == -b ) && FreeQ[{a, c}, t],

(*	 3.  one delta function	*)
dualFT[ c_. Delta[t_ + t0_.], t_, w_, __ ] :>
	c Exp[I t0 w] /;
	FreeQ[{c, t0}, t],

(*	 4.  impulse train		*)
dualFT[ x_. Periodic[T_, t_][y_. Delta[t_ + a_.]], t_, w_, rest__ ] :> 
	(1 / Abs[T]) Exp[I a w] *
	  Periodic[2 Pi / T, w][ dualFT[x y, t, w, rest] ] /;
	FreeQ[{a, n, y, T}, t],

dualFT[ x_. Summation[n_Symbol, -Infinity, Infinity, 1][
		      y_. Delta[t_ + a_. + n_ T_.]],
	t_, w_, rest__ ] :> 
	(1 / Abs[T]) Exp[I a w] *
	  Summation[n, -Infinity, Infinity, 1][
	    substitute[ dualFT[x y, t, w, rest], w, w + 2 Pi n / T ] ] /;
	FreeQ[{a, n, T}, t] && FreeQ[y, n],

(*	 5.  pulse functions		*)
dualFT[ CStep[t_ + a_] - CStep[t_ + b_] + x_., t_, w_, rest__ ] :>
	2 a Sinc[a w] + dualFT[x, t, w, rest] /;
	FreeQ[a, t] && (a == -b),

dualFT[ x_. + c_. ( CStep[t_ + a_.] - CStep[t_ + b_.] ), t_, w_, rest__ ] :>
	dualFT[c CPulse[Abs[b - a], t - Min[-a, -b]], t, w, rest] +
	  dualFT[x, t, w, rest] /;
	FreeQ[{a, b}, t],

dualFT[ CPulse[T_, t_ + a_.], t_, w_, __ ] :>
	T Exp[-I w T / 2] Exp[I a w] Sinc[w T / 2] /;
	FreeQ[{a, T}, t],

(*	 6.  two-sided Gaussian		*)
dualFT[ Exp[a_. t_^2], t_, w_, s_, op_, rest___ ] :>
	Block [ {},
		Assuming[ Negative[a], op ];
		Sqrt[Pi] Exp[w^2 / (4 a)] / Sqrt[-a] ] /;
	FreeQ[a, t] && mustbenegative[a],

(*	 7.  cosine x over x in time	*)
dualFT[ Cos[a_. t_] / t_, t_, w_, __ ] :>
	Abs[a] I ( CStep[-Abs[a] - w] - CStep[w - Abs[a]] ) /;
	FreeQ[a, t],

(*	 8.  two-sided cosine		*)
dualFT[ Cos[w0_. t_ + b_.], t_, w_, __ ] :>
	Exp[I w b / w0] Pi ( Delta[w - w0] + Delta[w + w0] ) /;
	FreeQ[{b, w0}, t],

(*	 9.  two-sided sine		*)
dualFT[ Sin[w0_. t_ + b_.], t_, w_, __ ] :>
	Exp[I w b / w0] Pi / I ( Delta[w - w0] - Delta[w + w0] ) /;
	FreeQ[{b, w0}, t],

(*	10.  two-sided sinc		*)
dualFT[ Sin[a_. t_] / t_, t_, w_, __ ] :> 
	Pi CPulse[2 a, w + a] /;
	FreeQ[a, t],

dualFT[ Sinc[a_. t_ + b_.], t_, w_, __ ] :> 
	Exp[I w b / a] Pi CPulse[2 a, w + a] / a /;
	FreeQ[{a,b}, t],	(* CPulse[2a, w+a] = CStep[w+a] - CStep[w-a] *)

dualFT[ (Sinc[a_. t_ + b_.])^2, t_, w_, __ ] :> 
	Exp[I w b / a] (Pi / a)^2 *
	  ( Unit[-2][w + 2 a] - 2 Unit[-2][w] + Unit[-2][w - 2 a] ) /;
	FreeQ[{a,b}, t],

(*	11.  other two-sided forms	*)
dualFT[ Exp[-Abs[a_. t_]] Sinc[a_. t_], t_, w_, __ ] :>
	ArcTan[ 2 a^2 / w^2 ] / a /;
	FreeQ[a, t],

dualFT[ Sign[t_ + t0_.], t_, w_, __ ] :>
	2 I Exp[I t0 w] / w /;
	FreeQ[t0, w],

dualFT[ 1 / t_, t_, w_, __ ] :> I Pi Sign[w],

dualFT[ BesselJ[0, a_. t_], t_, w_, __ ] :>
	CPulse[2 a, w + a] / ( Pi Sqrt[a^2 - w^2] ) /;
	FreeQ[a, t],

dualFT[ CStep[t_ + t0_.], t_, w_, __ ] :>
	I Exp[I t0 w] / w  +  Pi Delta[w] /;
	FreeQ[t0, t],	(* because Pi Exp[I t0 w] Delta[w] <--> Pi Delta[w] *)

dualFT[ CStep[-t_ + t0_.], t_, w_, __ ] :>
	-I Exp[-I t0 w] / w  +  Pi Delta[w] /;
	FreeQ[t0, t],	

(*  B.  Properties  *)
(*	1.  homogeneity		*)
dualFT[ a_ x_, t_, w_, rest__ ] :> 
	a dualFT[ x, t, w, rest ] /; FreeQ[a, t],

(*	2.  additivity		*)
dualFT[ a_ + b_, t_, w_, rest__ ] :> 
	dualFT[ a, t, w, rest ] + dualFT[ b, t, w, rest ],

dualFT[ Summation[i_, il_, iu_, inc_][f_], t_, w_, rest___ ] :>
	Summation[i, il, iu, inc][ dualFT[f, t, w, rest] ] /;
	FreeQ[{i, il, iu, inc}, t],

(*	3.  one-sided transform	*)
dualFT[ x_. CStep[t_ + t0_], t_, w_, rest__ ] :>
	Exp[- I w t0] dualFT[ ( x /. t -> (t - t0)) CStep[t], t, w, rest ] /;
	FreeQ[t0, t],

(*	4.  derivative		*)
dualFT[ Derivative[k_][x_][t_], t_, w_, rest__ ] :>
	(I w)^k dualFT[ x[t], t, w, rest ] /;
	FreeQ[k, t],

(*	5.  integration		*)
dualFT[ Integrate[x_, {tau_, -Infinity, t_}], t_, w_, rest__ ] :>
	Block [	{trans},
		trans = dualFT[x, tau, w, rest];
		trans ( 1/(I w) + Pi Delta[w] ) ],

(*	6.  multiply by time	*)
dualFT[ t_^a_. x_, t_, w_, s_, op_, rest___ ] :> 
	Block [	{dialogue},
		dialogue = SameQ[ Replace[Dialogue, op], All ];
		Assuming[ Positive[a], dialogue ];
		If [ dialogue, Print[ "where ", a, " is an integer" ] ];
		I^a derivative[ dualFT[x, t, w, s, op, rest], w, a ] ] /;
	FreeQ[a, t] && Implies[ NumberQ[a], IntegerQ[a] && a > 0 ],

(*	7.  divide by time	  *)
dualFT[ x_ / t_^k_., t_, w_, rest__ ] :> 
	-I integrate[ dualFT[x / t^(k-1), t, w, rest], {w, -Infinity, w} ] +
	  I Pi Limit[x, t -> 0] /;
	N[ k >= 1 ] && ! InfinityQ[ Limit[x, t -> 0] ],

(*	8.  exponential raised to an imaginary power times a function  *)
dualFT[ x_. Exp[ Complex[0, w0_] w1_. t_ + restexp_. ], t_, w_, rest__ ] :>
	substitute[ dualFT[x Exp[restexp], t, w, rest], w, w - w0 w1 ] /;
	FreeQ[{w0, w1}, t],

(* 	9.  convolution		*)
dualFT[ CConvolve[t_][x_, restconv__], t_, w_, rest__ ] :> 
	Apply[ Times, Map[ dualFT[#1, t, w, rest]&, { x, restconv } ] ],


(*  C.	Strategies		*)
(*	1.  expand expression	*)
dualFT[ x_, t_, w_, s_, oplist_, state_, rest___ ] :>
	dualFT [ Expand[x], t, w, s, oplist,
		 SetStateField[state, expandfield, False], rest ] /;
	GetStateField[state, expandfield],

(*	2.  function times a window	*)
dualFT[ x_ CPulse[l_, t_ + a_.], t_, w_, __ ] :>
	Integrate[ x Exp[- I w t], {t, -a, -a + l} ] /;
	FreeQ[{a, l}, t],

(*	3.  Apply the definition if Definition options is enabled  *)
dualFT[ x_, t_, w_, s_, op_, state_, dir_ ] :>
	Block [	{newstate, trans},
		newstate = SetStateField[state, definitionfield, False];
		trans = Integrate[x Exp[- I w t],
				  {t, -Infinity, Infinity}];
		If [ SameQ[Head[trans], Integrate],
		     dualFT[x, t, w, s, op, newstate, dir],
		     TransformDialogue[ Definition, op, Integrate,
					x, trans ] ] ] /;
	GetStateField[state, definitionfield] && Replace[Definition, op],

(*	4.  Try a forward or inverse Laplace transform if all else fails  *)
dualFT[ x_, t_, w_, s_, oplist_, state_, dir_ ] :> 
	Block [	{ltrans, newstate},
		ltrans = LaPlace[ x, t, s,
				  { Dialogue -> False } ~Join~ oplist ];
		If [ InformUserQ[oplist],
		     Print[ "( after taking the Laplace transform of " ];
		     Print[ "  ", x ];
		     Print[ "  with respect to ", t, " and" ];
		     Print[ "  adjusting the result )" ] ];
		newstate = SetStateField[state, forlaplacefield, False];
		If [ SameQ[Head[ltrans], LTransData],
		     If [ ROCincludesImagAxis[ ltrans, op ],
			  ( TheFunction[ltrans] /. s -> I w ),
		          dualFT[ x, t, w, s, oplist, newstate, dir ] ],
		     MapAll[ LtoCTFT[#1, t, w, s, oplist, newstate, dir]&,
			     ltrans ] ] ] /;
	GetStateField[state, forlaplacefield],

dualFT[ x_, w_, t_, s_, op_, state_, dir_ ] :>
	Block [	{invltrans, newstate, newx},
		newx = (x /. w -> (s / I));
		invltrans = InvLaPlace[ 2 Pi newx, s, t,
					{ Dialogue -> False } ~Join~ op ];
		If [ InformUserQ[op],
		     Print[ "( after taking the inverse Laplace transform of" ];
		     Print[ "  ", newx /. s -> "s" ];
		     Print[ "  which yields" ];
		     Print[ "  ", invltrans ];
		     Print[ "  and adjusting the result )" ] ];
		newstate = SetStateField[state, invlaplacefield, False];
		If [ FreeQ[invltrans, s],
		     substitute[invltrans, t, -t],   (* FT uses L^-1 so t=-t *)
		     dualFT[ x, w, t, s, op, newstate, dir ] ] ] /;
	GetStateField[state, invlaplacefield]

}


(*  E N D     P A C K A G E  *)

End[]
EndPackage[]

If [ TrueQ[ $VersionNumber >= 2.0 ],
     On[ General::spell ];
     On[ General::spell1 ] ];


(*  H E L P     I N F O R M A T I O N  *)

Combine[ SPfunctions, { CTFTransform, InvCTFTransform } ]
Protect[ CTFTransform, InvCTFTransform ]


(*  E N D I N G     M E S S A G E  *)

Print[ "The continuous Fourier transform (CTFT) functions are loaded." ]
Null
